package spinal_loongarch_core132

import scala.collection.mutable

import spinal.core._
import spinal.lib._

case class Inst(name:String
               ,code:String
               ,dest:String=""
               ,srcs:Array[String]=Array[String]()
               ,icls:String=""
               ,imm:Boolean=false
               ,sign:Boolean=false
               ,link:Boolean=false
               ,cond:Option[UInt=>Bool] = None
               ,plv:Boolean=false){
    val name_as_var:String = s"""${name.replace('.','_').toLowerCase()}"""
    val m:String = code.replace('0','1').replace('?','0')
    val v:String = code.replace('?','0')
    val icls_parts:Array[String] = icls.split("""\.""")
    def isMe(inst:UInt):Bool = {
        val flag:Bool = (inst & U(m)) === U(v)
        if(cond.isEmpty) flag else flag && (cond.get)(inst)
    }
}
class IClsNode(val parent:IClsNode,val name:String){
    val childs = mutable.Map[String,IClsNode]()
    var v_code:Int = 0
    var s_code:String = ""
    var w_code:Int = 0
    var lsb   :Int = 0
    def build(lsb:Int=0):Unit = {
        this.lsb    = lsb
        val keys = childs.keys.toArray
        w_code = if(keys.length <= 1) 0 else (keys.length-1).toBinaryString.length
        Range(0,keys.length).foreach((i:Int)=>{
                val child = childs(keys(i))
                val s:String = i.toBinaryString
                if(w_code == 0){
                    child.s_code = s_code
                    child.v_code = 0
                }
                else{
                    child.v_code = i
                    child.s_code = "0" * (w_code - s.length) + s + s_code
                }
            }
        )
        childs.values.foreach(_.build(lsb + w_code))
    }
    def alignCode(width:Int):Unit = {
        if(name == "$" && s_code.length < width)
            s_code = "0"*(width - s_code.length) + s_code
        childs.values.foreach(_.alignCode(width))
    }
    def maxWidth:Int = {
        if(childs.size == 0)return lsb + w_code
        return childs.values.map(_.maxWidth).max
    }
    def find(parts:Array[String],depth:Int=0):IClsNode = {
        if(depth >= parts.length)return this
        return childs(parts(depth)).find(parts,depth+1)
    }
    def show():Unit = {
        println(s_code)
        for((k,v) <- childs){
            println(s"""${k}:${v.lsb}->""")
            v.show()
        }
    }
}
object Instructions{
    val doc:String = """
            | Instruction encoding table for LoongArch32-Primary
            |"""
    val A = Array
    val infos:Array[Inst] = Array(
         Inst("RDCNTID.W","0000_0000_0000_00000_11000_?????_00000",dest="rj",icls="alu.t.id")
        ,Inst("RDCNTVL.W","0000_0000_0000_00000_11000_00000_?????",dest="rd",icls="alu.t.vl")
        ,Inst("RDCNTVH.W","0000_0000_0000_00000_11001_00000_?????",dest="rd",icls="alu.t.vh")
        ,Inst("ADD.W"    ,"0000_0000_0001_00000_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.a.add")
        ,Inst("SUB.W"    ,"0000_0000_0001_00010_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.a.sub")
        ,Inst("SLT"      ,"0000_0000_0001_00100_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.a.slt",sign=true)
        ,Inst("SLTU"     ,"0000_0000_0001_00101_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.a.slt")
        ,Inst("NOR"      ,"0000_0000_0001_01000_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.l.nor")
        ,Inst("AND"      ,"0000_0000_0001_01001_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.l.and")
        ,Inst("OR"       ,"0000_0000_0001_01010_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.l.or" )
        ,Inst("XOR"      ,"0000_0000_0001_01011_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.l.xor")
        ,Inst("SLL.W"    ,"0000_0000_0001_01110_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.s.l")
        ,Inst("SRL.W"    ,"0000_0000_0001_01111_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.s.r")
        ,Inst("SRA.W"    ,"0000_0000_0001_10000_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="alu.s.r",sign=true)
        ,Inst("MUL.W"    ,"0000_0000_0001_11000_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="mul.l")
        ,Inst("MULH.W"   ,"0000_0000_0001_11001_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="mul.h",sign=true)
        ,Inst("MULH.WU"  ,"0000_0000_0001_11010_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="mul.h")
        ,Inst("DIV.W"    ,"0000_0000_0010_00000_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="d/m.div",sign=true)
        ,Inst("MOD.W"    ,"0000_0000_0010_00001_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="d/m.mod",sign=true)
        ,Inst("DIV.WU"   ,"0000_0000_0010_00010_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="d/m.div")
        ,Inst("MOD.WU"   ,"0000_0000_0010_00011_?????_?????_?????",dest="rd",srcs=A("rj","rk"),icls="d/m.mod")
        ,Inst("BREAK"    ,"0000_0000_0010_10100_?????_?????_?????")
        ,Inst("SYSCALL"  ,"0000_0000_0010_10110_?????_?????_?????")
        ,Inst("SLLI.W"   ,"0000_0000_010000_001_?????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.s.l")
        ,Inst("SRLI.W"   ,"0000_0000_010001_001_?????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.s.r")
        ,Inst("SRAI.W"   ,"0000_0000_010010_001_?????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.s.r",sign=true)
        ,Inst("SLTI"     ,"0000_0010_00_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.a.slt",sign=true)
        ,Inst("SLTUI"    ,"0000_0010_01_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.a.slt")
        ,Inst("ADDI.W"   ,"0000_0010_10_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.a.add")
        ,Inst("ANDI"     ,"0000_0011_01_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.l.and")
        ,Inst("ORI"      ,"0000_0011_10_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.l.or")
        ,Inst("XORI"     ,"0000_0011_11_??????_??????_?????_?????",dest="rd",srcs=A("rj"),imm=true,icls="alu.l.xor")
        ,Inst("CSRRD"    ,"0000_0100_????_?????_?????_00000_?????",dest="rd"                  ,icls="csr.r",plv=true)
        ,Inst("CSRWR"    ,"0000_0100_????_?????_?????_00001_?????",dest="rd",srcs=A("rd")     ,icls="csr.w",plv=true)
        ,Inst("CSRXCHG"  ,"0000_0100_????_?????_?????_?????_?????",dest="rd",srcs=A("rd","rj"),icls="csr.x",plv=true,cond=Some((inst:UInt)=>inst(9 downto 6).orR))
        ,Inst("CACOP"    ,"0000_0110_00_??????_??????_?????_?????"          ,srcs=A("rj")                  ,plv=true)
        ,Inst("TLBSRCH"  ,"0000_0110_0100_10000_01010_00000_00000",plv=true)
        ,Inst("TLBRD"    ,"0000_0110_0100_10000_01011_00000_00000",plv=true)
        ,Inst("TLBWR"    ,"0000_0110_0100_10000_01100_00000_00000",plv=true)
        ,Inst("TLBFILL"  ,"0000_0110_0100_10000_01101_00000_00000",plv=true)
        ,Inst("ERTN"     ,"0000_0110_0100_10000_01110_00000_00000",plv=true)
        ,Inst("IDLE"     ,"0000_0110_0100_10001_?????_?????_?????",plv=true)
        ,Inst("INVTLB"   ,"0000_0110_0100_10011_?????_?????_?????",plv=true,srcs=A("rj","rk"))
        ,Inst("LU12I.W"  ,"0001_010_?????_?????_?????_?????_?????",dest="rd",icls="alu.l.lui")
        ,Inst("PCADDU12I","0001_110_?????_?????_?????_?????_?????",dest="rd",icls="alu.a.pcadd",imm=true)
        ,Inst("LL.W"     ,"0010_0000_????_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.w.ll")
        ,Inst("SC.W"     ,"0010_0001_????_?????_?????_?????_?????",dest="rd",srcs=A("rd","rj"),icls="mem.st.w.sc")
        ,Inst("LD.B"     ,"00101_00000_??_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.b",sign=true)
        ,Inst("LD.H"     ,"00101_00001_??_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.h",sign=true)
        ,Inst("LD.W"     ,"00101_00010_??_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.w",sign=true)
        ,Inst("ST.B"     ,"00101_00100_??_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="mem.st.b")
        ,Inst("ST.H"     ,"00101_00101_??_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="mem.st.h")
        ,Inst("ST.W"     ,"00101_00110_??_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="mem.st.w")
        ,Inst("LD.BU"    ,"00101_01000_??_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.b")
        ,Inst("LD.HU"    ,"00101_01001_??_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="mem.ld.h")
        ,Inst("PRELD"    ,"00101_01011_??_?????_?????_?????_?????"          ,srcs=A("rj")     ,icls="mem.pr"  )
        ,Inst("DBAR"     ,"0011_1000_0111_00100_?????_?????_?????"                            )
        ,Inst("IBAR"     ,"0011_1000_0111_00101_?????_?????_?????"                            )
        ,Inst("JIRL"     ,"01_0011_??????_?????_?????_?????_?????",dest="rd",srcs=A("rj")     ,icls="bru.j"   ,link=true)
        ,Inst("B"        ,"01_0100_??????_?????_?????_?????_?????"                            ,icls="bru.b.26")
        ,Inst("BL"       ,"01_0101_??????_?????_?????_?????_?????",dest="ra"                  ,icls="bru.b.26",link=true)
        ,Inst("BEQ"      ,"01_0110_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.eq")
        ,Inst("BNE"      ,"01_0111_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.ne")
        ,Inst("BLT"      ,"01_1000_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.lt",sign=true)
        ,Inst("BGE"      ,"01_1001_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.ge",sign=true)
        ,Inst("BLTU"     ,"01_1010_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.lt")
        ,Inst("BGEU"     ,"01_1011_??????_?????_?????_?????_?????"          ,srcs=A("rd","rj"),icls="bru.b.16.ge")
    )
    def getRD  (inst:UInt):UInt = inst( 4 downto  0)
    def getRJ  (inst:UInt):UInt = inst( 9 downto  5)
    def getRK  (inst:UInt):UInt = inst(14 downto 10)
    def getCode(inst:UInt):UInt = inst(14 downto  0)
    def getUI5 (inst:UInt):UInt = inst(14 downto 10)
    val tree = new IClsNode(null,"")
    def addICls(parts:Array[String]):Unit = {
        var cur:IClsNode = tree
        for(i <- 0 until parts.length){
            val part:String = parts(i)
            cur = cur.childs.getOrElseUpdate(part,new IClsNode(cur,part))
        }
        if(!cur.childs.contains("$"))
            cur.childs.put("$",new IClsNode(cur,"$"))
    }
    infos.foreach((i:Inst)=>addICls(i.icls_parts))
    tree.build()
    val w_opcode :Int = tree.maxWidth
    val w_subcode:Int = w_opcode - tree.w_code
    if(false)printf("Max op-code width:%d\n",w_opcode)
    def OpSubCode:UInt = UInt(w_subcode bits)
    tree.alignCode(w_opcode)
    def getOpCode(parts:Array[String]):UInt = {
        val node:IClsNode = tree.find(parts)
        val leaf:IClsNode = node.childs("$")
        val code:UInt = U(leaf.s_code.substring(0, leaf.s_code.length - tree.w_code))
        if(false){
            assert(code.getWidth == w_subcode)
            for(part <- parts)printf("%s--",part)
            printf("Got %s\n",code.toString)
        }
        return code
    }
    def select(op:UInt,icls:String,values:Map[String,UInt]):UInt = {
        val node:IClsNode = tree.find(icls.split("""\.""" ))
        def maskedValue(entry:(String,UInt)):UInt = {
            val (k,v):(String,UInt) = entry
            Mux(isSubOpCode(op,node,k),v,U(0))
        }
        return values.toArray.map(maskedValue).fold(U(0))(_|_)
    }
    def isSubOpCode(op:UInt,icls:String,subi:String):Bool = {
        val node:IClsNode = tree.find(icls.split("""\.""" ))
        return isSubOpCode(op,node,subi)
    }
    def isSubOpCode(op:UInt,node:IClsNode,subi:String):Bool = {
        val child:IClsNode = node.childs(subi)
        val lsb:Int = node.lsb - tree.w_code
        val w  :Int = node.w_code
        val s_code:String = child.s_code.substring(0, w)
        val opcode:UInt = op(lsb, w bits)
        return U(s_code) === opcode
    }
    def dumpSubOpCodes:Unit = {
        tree.show()
    }
    def isInst(inst:UInt,name:String):Bool = {
        infos.find(_.name == name).get.isMe(inst)
    }
}
class InstTypeInfo extends Bundle{
    val is_alu:Bool = Bool()
    val is_mul:Bool = Bool()
    val is_div:Bool = Bool()
    val is_bru:Bool = Bool()
    val is_mem:Bool = Bool()
    val is_csr:Bool = Bool()
    val is_cacop:Bool = Bool()
    val is_tlbsrch:Bool = Bool()
    val is_invtlb :Bool = Bool()
    val is_ertn:Bool = Bool()
    val link  :Bool = Bool()
    val sign  :Bool = Bool()
    val imm   :Bool = Bool()
}
class InstInfo extends Bundle{
    val valid :Bool = Bool()
    val rd    :UInt = UInt(5 bits)
    val rj    :UInt = UInt(5 bits)
    val rk    :UInt = UInt(5 bits)
    val dest  :UInt = UInt(5 bits)
    val grwr  :Bool = Bool()
    val use_rd:Bool = Bool()
    val use_rj:Bool = Bool()
    val use_rk:Bool = Bool()
    val trap_sys:Bool = Bool()
    val trap_brk:Bool = Bool()
    val type_plv:Bool = Bool()
    val itype:InstTypeInfo = new InstTypeInfo
    val subcode:UInt = Instructions.OpSubCode
}
class InstDecoder extends Component{
    case class InstDec(inst:UInt,val meta:Inst) extends Bundle{
        val is_me:Bool = meta.isMe(inst)
        setName(meta.name_as_var)
    }
    val I = Instructions
    val inst:UInt = in UInt(32 bits)
    val info:InstInfo = out(new InstInfo)
    val chk :Array[InstDec] = I.infos.map(InstDec(inst,_))
    val by_dest:Map[String,Array[InstDec]] = chk.groupBy(_.meta.dest)
    def anyOf(dec:Array[InstDec]):Bool = dec.map(_.is_me).fold(False)(_||_)
    val dest_is_rj:Bool = anyOf(by_dest("rj"))
    val dest_is_rd:Bool = anyOf(by_dest("rd"))
    val dest_is_ra:Bool = anyOf(by_dest("ra"))
    info.rd := I.getRD(inst)
    info.rj := I.getRJ(inst)
    info.rk := I.getRK(inst)
    info.valid := anyOf(chk)
    info.grwr := anyOf(chk.filter(_.meta.dest != ""))
    val ra:UInt = U(1, 5 bits)
    info.dest := ( Mux(dest_is_rj,info.rj,U(0))
                 | Mux(dest_is_rd,info.rd,U(0))
                 | Mux(dest_is_ra,     ra,U(0)))
    info.use_rd := anyOf(chk.filter(_.meta.srcs.exists(_ == "rd")))
    info.use_rj := anyOf(chk.filter(_.meta.srcs.exists(_ == "rj")))
    info.use_rk := anyOf(chk.filter(_.meta.srcs.exists(_ == "rk")))
    info.trap_sys := chk.find(_.meta.name == "SYSCALL").get.is_me
    info.trap_brk := chk.find(_.meta.name == "BREAK").get.is_me
    info.type_plv := anyOf(chk.filter(_.meta.plv))
    info.itype.is_mul := anyOf(chk.filter(_.meta.icls.startsWith("mul.")))
    info.itype.is_div := anyOf(chk.filter(_.meta.icls.startsWith("d/m.")))
    info.itype.is_alu := anyOf(chk.filter(_.meta.icls.startsWith("alu.")))
    info.itype.is_bru := anyOf(chk.filter(_.meta.icls.startsWith("bru.")))
    info.itype.is_mem := anyOf(chk.filter(_.meta.icls.startsWith("mem.")))
    info.itype.is_csr := anyOf(chk.filter(_.meta.icls.startsWith("csr.")))
    info.itype.is_cacop   := chk.find(_.meta.name == "CACOP").get.is_me
    info.itype.is_tlbsrch := chk.find(_.meta.name == "TLBSRCH").get.is_me
    info.itype.is_invtlb  := chk.find(_.meta.name == "INVTLB" ).get.is_me
    info.itype.is_ertn    := chk.find(_.meta.name == "ERTN" ).get.is_me
    info.itype.sign   := anyOf(chk.filter(_.meta.sign))
    info.itype.link   := anyOf(chk.filter(_.meta.link))
    info.itype.imm    := anyOf(chk.filter(_.meta.imm ))
    info.subcode := (chk
                   .filter(_.meta.icls != "")
                   .map((i:InstDec)=>Mux(i.is_me,I.getOpCode(i.meta.icls_parts),U(0)))
                   .reduce(_|_)
                   )
}

object GenerateVerilogForInstDecoder {
    def main(args: Array[String]): Unit = {
        SpinalConfig(mode = Verilog, removePruned = true, targetDirectory="./gen/rtl").generate(new InstDecoder())
    }
}
